package com.wsw.common.dao.impl;

import java.io.Serializable;
import java.math.BigInteger;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.persistence.Entity;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.stereotype.Repository;

import com.wsw.common.dao.IBaseDao;
import com.wsw.common.model.QueryResult;
import com.wsw.common.util.GenericsUtils;

@Repository
public class BaseDaoImpl<T> implements IBaseDao<T> {
	private Class<T> entityClass;// 泛型的实例
	@Autowired
	private SessionFactory sessionFactory;

	/**
	 * 通过反射获取类型
	 */
	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		entityClass = GenericsUtils.getSuperClassGenricType(this.getClass());
	}

	/**
	 * 获得当前事物的session
	 * 
	 * @return org.hibernate.Session
	 */
	public Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	@Override
	public Serializable save(T o) {
		if (o != null) {
			return getCurrentSession().save(o);
		}
		return null;
	}

	@Override
	public T getById(Class<T> clazz, Serializable id) {
		return (T) getCurrentSession().get(clazz, id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> loadAll() {
		String d = getEntityName(entityClass);
		return getCurrentSession().createQuery("from " + d).list();
	}

	@Override
	public T getByHql(String hql) {
		Query q = getCurrentSession().createQuery(hql);
		List<T> l = q.list();
		if (l != null && l.size() > 0) {
			return l.get(0);
		}
		return null;
	}

	@Override
	public T getByHql(String hql, Map<String, Object> params) {
		Query q = getCurrentSession().createQuery(hql);
		setParams(params, q);
		List<T> l = q.list();
		if (l != null && l.size() > 0) {
			return l.get(0);
		}
		return null;
	}

	@Override
	public void delete(T o) {
		if (o != null) {
			getCurrentSession().delete(o);
		}
	}

	@Override
	public void update(T o) {
		if (o != null) {
			getCurrentSession().update(o);
		}
	}

	@Override
	public void saveOrUpdate(T o) {
		if (o != null) {
			getCurrentSession().saveOrUpdate(o);
		}
	}

	@Override
	public List<T> find(String hql) {
		Query q = getCurrentSession().createQuery(hql);
		return q.list();
	}

	@Override
	public List<T> find(String hql, Map<String, Object> params) {
		Query q = getCurrentSession().createQuery(hql);
		setParams(params, q);
		return q.list();
	}

	@Override
	public QueryResult<T> find(String hql, Map<String, Object> params, int pageNo,
			int pageSize) {
		pageNo = pageNo < 1 ? 1 : pageNo;
		Long totalCount = count(hql, params);
		if (totalCount < 1) {
			return new QueryResult<T>();
		}
		int startIndex;
		startIndex = QueryResult.getStartOfPage(pageNo, pageSize);
		Query query1 = getCurrentSession().createQuery(hql);
		setParams(params, query1);
		List<T> list = query1.setFirstResult(startIndex)
				.setMaxResults(pageSize).list();
		return new QueryResult<T>(startIndex, totalCount.intValue(), pageSize, list);
	}

	@Override
	public QueryResult<T> find(String hql, int page, int rows) {
		Query q = getCurrentSession().createQuery(hql);
		return find(hql, null, page, rows);
	}

	@Override
	public Long count(String hql) {
		String countQueryString = "select count(*) "
				+ removeSelect(removeOrders(hql)+" ");
		Query q = getCurrentSession().createQuery(countQueryString);
		return (Long) q.uniqueResult();
	}

	@Override
	public Long count(String hql, Map<String, Object> params) {
		String countQueryString = "select count(*) "
				+ removeSelect(removeOrders(hql)+" ");
		Query q = getCurrentSession().createQuery(countQueryString);
		setParams(params, q);
		return (Long) q.uniqueResult();
	}

	private void setParams(Map<String, Object> params, Query q) {
		if (params != null && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
	}

	@Override
	public int executeHql(String hql) {
		Query q = getCurrentSession().createQuery(hql);
		return q.executeUpdate();
	}

	@Override
	public int executeHql(String hql, Map<String, Object> params) {
		Query q = getCurrentSession().createQuery(hql);
		setParams(params, q);
		return q.executeUpdate();
	}

	@Override
	public List<Map> findBySql(String sql) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		return q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
	}

	@Override
	public List<Map> findBySql(String sql, int page, int rows) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		return q.setFirstResult((page - 1) * rows).setMaxResults(rows)
				.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
	}

	@Override
	public List<Map> findBySql(String sql, Map<String, Object> params) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		if (params != null && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
	}

	@Override
	public List<Map> findBySql(String sql, Map<String, Object> params,
			int page, int rows) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		if (params != null && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.setFirstResult((page - 1) * rows).setMaxResults(rows)
				.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
	}

	@Override
	public int executeSql(String sql) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		return q.executeUpdate();
	}

	@Override
	public int executeSql(String sql, Map<String, Object> params) {
		SQLQuery q = getCurrentSession().createSQLQuery(sql);
		if (params != null && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return q.executeUpdate();
	}

	@Override
	public BigInteger countBySql(String sql) {
		String countQueryString = "select count(*) "
				+ removeSelect(removeOrders(sql) + " ");
		SQLQuery q = getCurrentSession().createSQLQuery(countQueryString);
		return (BigInteger) q.uniqueResult();
	}

	@Override
	public BigInteger countBySql(String sql, Map<String, Object> params) {
		String countQueryString = "select count(*) "
				+ removeSelect(removeOrders(sql) + " ");
		SQLQuery q = getCurrentSession().createSQLQuery(countQueryString);
		if (params != null && !params.isEmpty()) {
			for (String key : params.keySet()) {
				q.setParameter(key, params.get(key));
			}
		}
		return (BigInteger) q.uniqueResult();
	}

	/**
	 * 调用存储过程，如有返回值则获得返回值
	 * 
	 * @param sql
	 * @param in_params
	 * @param out_paramtypes
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public String[] executeProc(String sql, List in_params, int[] out_paramtypes)
			throws Exception {
		String[] ret = null;
		Connection conn =getCurrentSession().disconnect();
		CallableStatement cst;
		int i = 1;
		cst = conn.prepareCall(sql);
		if (in_params != null) {
			for (; i <= in_params.size(); i++) {
				SqlParameter param = (SqlParameter) in_params.get(i - 1);
				int type = param.getSqlType();
				String value = param.getName();
				switch (type) {
				case Types.CHAR:
					cst.setString(i, value);
					break;
				case Types.VARCHAR:
					cst.setString(i, value);
					break;
				case Types.INTEGER:
					cst.setInt(i, Integer.parseInt(value));
					break;
				case Types.DOUBLE:
					cst.setDouble(i, Double.parseDouble(value));
					break;
				case Types.BIGINT:
					cst.setLong(i, Long.parseLong(value));
					break;
				default:
					throw new Exception("不支持的数据类型");
				}
			}
		}
		int ii = i;
		if (out_paramtypes != null) {
			ret = new String[out_paramtypes.length];
			for (int j = 0; j < out_paramtypes.length; j++) {
				cst.registerOutParameter(ii + j, out_paramtypes[j]);
			}
		}
		cst.execute();
		if (out_paramtypes != null) {
			for (int j = 0; j < out_paramtypes.length; j++) {
				ret[j] = cst.getString(ii + j);
			}
		}
		cst.close();
		return ret;
	}

	/**
	 * 通过存储过程获得list
	 * 
	 * @param sql
	 * @param in_params
	 * @return
	 */
	@SuppressWarnings( { "unchecked",  "rawtypes" })
	public List getProcList(String sql, List in_params) throws Exception {
		List ret = null;
		Connection conn = getCurrentSession().disconnect();
		CallableStatement cst;
		int i = 1;
		cst = conn.prepareCall(sql);
		if (in_params != null) {
			for (; i <= in_params.size(); i++) {
				SqlParameter param = (SqlParameter) in_params.get(i - 1);
				int type = param.getSqlType();
				String value = param.getName();
				switch (type) {
				case Types.CHAR:
					cst.setString(i, value);
					break;
				case Types.VARCHAR:
					cst.setString(i, value);
					break;
				case Types.INTEGER:
					cst.setInt(i, Integer.parseInt(value));
					break;
				case Types.DOUBLE:
					cst.setDouble(i, Double.parseDouble(value));
					break;
				case Types.BIGINT:
					cst.setLong(i, Long.parseLong(value));
					break;
				default:
					throw new Exception("不支持的数据类型");
				}
			}
		}
		if (cst.execute()) {
			ret = new ArrayList();
			ResultSet rs = cst.getResultSet();
			ResultSetMetaData rsmd = rs.getMetaData();
			int colCount = rsmd.getColumnCount();
			while (rs.next()) {
				for (int j = 1; j <= colCount; j++) {
					ret.add(rs.getObject(j));
				}
			}
		}
		cst.close();
		return ret;
	}
/*	*//**
	 * 通过存储过程获得游标
	 * 
	 * @param sql
	 * @param in_params
	 * @return
	 *//*
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<Map> getProcCursor(String sql, List in_params) throws Exception {
		List ret = null;
		Connection conn = getCurrentSession().disconnect();
		CallableStatement cst;
		int i = 1;
		cst = conn.prepareCall(sql);
		if (in_params != null) {
			for (; i <= in_params.size(); i++) {
				SqlParameter param = (SqlParameter) in_params.get(i - 1);
				int type = param.getSqlType();
				String value = param.getName();
				switch (type) {
				case Types.CHAR:
					cst.setString(i, value);
					break;
				case Types.VARCHAR:
					cst.setString(i, value);
					break;
				case Types.INTEGER:
					cst.setInt(i, Integer.parseInt(value));
					break;
				case Types.DOUBLE:
					cst.setDouble(i, Double.parseDouble(value));
					break;
				case Types.BIGINT:
					cst.setLong(i, Long.parseLong(value));
					break;
				default:
					throw new Exception("不支持的数据类型");
				}
			}
		}
		cst.registerOutParameter(in_params.size() + 1,
				oracle.jdbc.OracleTypes.CURSOR);// 这个返回结果集
		cst.execute();
		ret = new ArrayList();
		ResultSet rs = (ResultSet) cst.getObject(in_params.size() + 1);
		ResultSetMetaData rsmd = rs.getMetaData();// 取得data
		int colCount = rsmd.getColumnCount();// 列数
		Map ma = new HashMap();
		while (rs.next()) {
			for (int j = 1; j <= colCount; j++) {
				// key 列名字 value 值
				ma.put(rsmd.getColumnName(j), rs.getObject(j));
			}
		}
		ret.add(ma);
		cst.close();
		return ret;
	}*/
	
	/**
	 * 获取实体的名称
	 * 
	 * @param <E>
	 * @param clazz
	 *            实体类
	 * @return
	 */
	protected static <E> String getEntityName(Class<E> clazz) {
		String entityname = clazz.getSimpleName();
		Entity entity = clazz.getAnnotation(Entity.class);
		if (entity != null && entity.name() != null
				&& !"".equals(entity.name())) {
			entityname = entity.name();
		}
		return entityname;
	}

	/**
	 * 去除hql的select子句
	 * 
	 * @param hql
	 * @return
	 */
	private static String removeSelect(String hql) {
		int beginPos = hql.toLowerCase().indexOf("from");
		return hql.substring(beginPos);
	}

	/**
	 * 去除hql的order by子句
	 * 
	 * @param hql
	 * @return
	 */
	private static String removeOrders(String hql) {
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*",
				Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}
}
